package com.zx.idc.backend.gui.api.watch;

import com.zx.idc.backend.gui.api.ftp.BaseFtpClientConfig;
import com.zx.idc.backend.gui.api.ftp.FTPThreadPoolFactory;
import com.zx.idc.backend.gui.api.ftp.FtpOperator;
import com.zx.idc.backend.gui.api.ftp.Stat;
import com.zx.idc.backend.gui.api.ftp.watch.FtpWatchConfig;
import com.zx.idc.backend.gui.api.watch.handler.base.FtpUploadResultHandelProvider;
import com.zx.idc.ds.sys.service.ISysGlobalStatusService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.zx.idc.common.lang.StringUtil.append;

@Component
public class DefaultFtpWatch {
    //结果列表分隔的阈值
    private final static int PARTITION_NUM = 1000;
    //检查线程执行结果的线程执行频率 单位:毫秒
    private final static int CHECK_THREAD_EXECUTE_RESULT_FREQUENCY = 5000;


    private final Logger log = LoggerFactory.getLogger(DefaultFtpWatch.class);

    @Autowired
    private FtpWatchConfig ftpWatchConfig;

    @Autowired
    private ISysGlobalStatusService iSysGlobalStatusService;

    @Autowired
    private FtpUploadResultHandelProvider ftpUploadResultHandelProvider;

    @Autowired
    private FtpOperator ftpOperator;

    @Autowired
    @Qualifier("ftpClientConfig")
    private BaseFtpClientConfig ftpClientConfig;

    @PostConstruct
    void init() {
        initWatch();
    }

    /**
     * 启动目录扫描器
     */
    private void initWatch() {
        if (ftpOperator == null) {
            log.error("获取sftp操作对象失败,请配置FTP相关信息");
            return;
        }
        if (!ftpWatchConfig.isEnable()) {
            log.warn("ftp监听器未启动,如需要请把 enable 设置为true");
            return;
        }
        String watchPath = append(
            ftpClientConfig.getBasePath(),
            "/",
            ftpWatchConfig.getPath());
        //初始化目录锁 不存在则添加
        iSysGlobalStatusService.selectFtpDiskLockIsExists();
        log.debug("ftp目录扫描线程启动,时间:{}.", LocalDateTime.now());
        //启动一个调度任务循环扫描指定目录
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
                List<Stat> stats2 = null;
                try {
                    //1.监听目录
                    log.debug("扫描执行时间:{}.", LocalDateTime.now());
                    List<Stat> stats = ftpOperator.lsR(watchPath);

                    //扫描到文件
                    if (stats == null || stats.isEmpty()) {
                        return;
                    }

                    //获取目录锁
                    if (!iSysGlobalStatusService.getFtpDiskLock()) {
                        return;
                    }
                    try {
                        log.debug("获取锁,执行时间:{}.", LocalDateTime.now());
                        stats2 = ftpOperator.lsR(watchPath);
                        //再次扫描文件 确保文件是最新的
                        if (stats2 == null || stats2.isEmpty()) {
                            //释放锁
                            iSysGlobalStatusService.releaseFtpDiskLock();
                            log.debug("释放锁成功");
                            return;
                        }
                    } catch (Exception e) {
                        //防止网络异常死锁
                        iSysGlobalStatusService.releaseFtpDiskLock();
                        return;
                    }

                } catch (Exception e) {
                    log.error("ftp扫描目录线程出现未知错误,跳过本次扫描", e.getMessage());
                    return;
                }

                //使用多线程执行任务，执行完成再释放锁 多线程做法
                try {
                    CountDownLatch countDownLatch = new CountDownLatch(stats2.size());
                    ExecutorService fixedThreadExecutor = FTPThreadPoolFactory.getFixedThreadExecutor();
                    stats2.forEach(stat ->
                        fixedThreadExecutor.execute(() -> {
                            try {
                                ftpUploadResultHandelProvider.execute(stat);
                            } catch (Throwable e) {
                                log.error("处理任务出现未知出错", e);
                            } finally {
                                countDownLatch.countDown();
                            }
                        })
                    );

                    try {
                        //等待全部任务执行完成
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        log.error("等待全部任务执行结束异常", e);
                    }
                } catch (Exception e) {
                    log.error("执行任务出错", e);
                } finally {
                    //任务处理完成 释放目录锁
                    iSysGlobalStatusService.releaseFtpDiskLock();
                }

                //需要添加目录锁死锁监测机制？ 启动一个任务调度 定期扫描锁 获取锁时间超过设定时间自动释放锁 待定
            },
            0,
            ftpWatchConfig.getInterval(),
            ftpWatchConfig.getUnit()
        );
    }

    /**
     * 释放资源，线程池
     */
    @PreDestroy
    public void threadPoolDestroy() {
        try {
            log.info("ftp watch will shutdown... ftp thread pool will shutdown..");
            FTPThreadPoolFactory.getSingleThreadExecutor().shutdown();
            log.info("ftp watch will shutdown... ftp thread pool will shutdown success");
        } catch (Exception e) {
            log.error("ftp thread pool shutdown failed.", e);
        }
    }

    @PreDestroy
    public void releaseFtpDiskLockDestroy() {
        try {
            log.info("ftp watch will shutdown... will releaseFtpDiskLock");
            iSysGlobalStatusService.releaseFtpDiskLock();
            log.info("ftp watch has shutdown.releaseFtpDiskLock success");

        } catch (Exception e) {
            log.error("releaseFtpDiskLock failed.", e);
        }
    }

}
